#include "myc.h"

struct type *types=NULL;

struct type *add_type(char *name,int size) {
	struct type *me=calloc(1,sizeof(*me));
	me->next=types;
	me->name=name;
	me->size=size;
	return types=me;
}

struct type *find_type(struct token *tok) {
	struct type *me=types;
	while (me) {
		if (tokis(tok,me->name)) return me;
		me=me->next;
	}
	return NULL;
}

struct type *get_type(char *str) {
	struct type *me=types;
	while (me) {
		if (!strcmp(str,me->name)) return me;
		me=me->next;
	}
	return NULL;
}

struct mem *find_member(struct type *type,struct token *tok) {
	struct mem *me=type->mems;
	while (me) {
		if (tokis(tok,me->name)) return me;
		me=me->next;
	}
	return NULL;
}

struct type *def_struct() {
	if (!is_letter(*pass->str)) error("invalid struct name");
	struct type *me=add_type(strndup(pass->str,pass->len),0);
	if (!tokis(pass->next,"{")) error("missing {");
	pass=pass->next->next;
	struct mem *new;
	while (!tokis(pass,"}")) {
		new=calloc(1,sizeof(*new));
		new->type.bt=pass_type();
		new->type.lv=1;
		while(tokis(pass,"*")) {
			new->type.pt++;
			pass=pass->next;
		}
		if (!is_letter(*pass->str)) error("invalid member name");
		if (find_member(me,pass)) error("duplicate member definition");
		new->name=strndup(pass->str,pass->len);
		if (new->type.bt==me && new->type.pt==0) error("struct cannot contain itself");
		new->offset=me->size;
		int siz=size(new->type);
		if (tokis(pass->next,"[")) {
			new->type.lv=0;
			new->type.pt++;
			if (!isdigit(*pass->next->next->str)) error("invalid array size");
			if (!tokis(pass->next->next->next,"]")) error("invalid array def");
			siz*=toktoint(pass->next->next);
			pass=pass->next->next->next;
		}
		me->size+=siz;
		new->next=me->mems;
		me->mems=new;
		if (!tokis(pass->next,";")) error("missing ;");
		pass=pass->next->next;
	}
	pass=pass->next;
	return me;
}

struct type *pass_type() {
	struct type *ret;
	if (tokis(pass,"struct")) {
		pass=pass->next;
		if ((ret=find_type(pass))) {
			pass=pass->next;
			return ret;
		} else return def_struct(); 
	} else {
		if (!(ret=find_type(pass))) error("invalid type");
		pass=pass->next;
		return ret;
	}
}

struct type *tmp_type(struct token **tok) {
	struct type *ret;
	if (tokis(*tok,"struct")) {
		*tok=(*tok)->next;
		ret=find_type(*tok);
		*tok=(*tok)->next;
		if (tokis(*tok,"{")) *tok=endbr(*tok,NULL)->next;
		return ret;
	} else {
		if (!(ret=find_type(*tok))) error("invalid type");
		*tok=(*tok)->next;
		return ret;
	}
}

int istypdef(struct token *tok) {
	return find_type(tok) || tokis(tok,"struct");
}

int size(struct vtype vt) {
	if (vt.pt) return PTR_SIZE;
	return vt.bt->size;
}

void freetypes() {
	while (types) {
		struct mem *tmem,*mem=types->mems;
		while (mem) {
			tmem=mem->next;
			free(mem->name);
			free(mem);
			mem=tmem;
		}
		struct type *ttype=types->next;
		free(types->name);
		free(types);
		types=ttype;
	}
}
